﻿
#ifdef _WIN32
#pragma warning(disable : 4702)
#define DIR_SEPARATOR '\\'
#else
#include <dirent.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define DIR_SEPARATOR '/'
#endif
#include "netpcap_def.h"
#include <chrono>
#include <cstdio>
#include <errno.h>
#include <iostream>

#include "netpcap_file.h"
#include "netpcap_log.hpp"

namespace secsmart_pcap
{

/**
 * @brief 获取时间戳,格式为“年月日时分秒”
 *
 * @return bool
 *
 */
std::string netpcap_file::pcapfile_timestamp()
{
    char tmp[32] = {0};
    time_t now = time(nullptr);
    struct tm *t = localtime(&now);
    (void)sprintf(tmp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min,
                  t->tm_sec);
    return std::string(tmp);
}

/**
 * @brief 设置单个抓包文件大小
 * @param uint32_t nums    文件包的个数来限制
 * @return bool
 *
 */
void netpcap_file::pcapfile_limits_nums(std::uint32_t nums)
{
    if (nums > NETPCAP_FILEMAXNUMS)
        m_file_limits_ = NETPCAP_FILEMAXNUMS;
    else
        m_file_limits_ = nums;
};

/**
 * @brief 抓包文件目录读取
 * @param const std::string& filepath,   文件完整路径
 *
 * @return bool
 *
 */
bool netpcap_file::pcapfile_dir_list(const std::string &dirPath, std::string suffix)
{
    std::string path;
    m_file_vector_.clear();
#if _WIN32
    intptr_t hfile;
    struct _finddata_t fileinfos;

    if ((hfile = _findfirst(path.assign(dirPath).append("\\*").c_str(), &fileinfos)) != -1) {
        do {
            // 判断是否为文件夹
            if ((fileinfos.attrib & _A_SUBDIR)) {
                if (strcmp(fileinfos.name, ".") != 0 && strcmp(fileinfos.name, "..") != 0) {
                    LOG_DEBUG("director list subdir={}.", fileinfos.name);
                    // m_file_vector_.push_back(path.assign(path).append("/").append(fileinfos.name));
                    // 递归当前文件夹
                    pcapfile_dir_list(path.assign(dirPath).append("/").append(fileinfos.name), suffix);
                }
            }
            // 文件处理
            else {
                // 文件名,匹配所有文件
                if (strcmp(suffix.c_str(), "*") == 0)
                    m_file_vector_.push_back(path.assign(dirPath).append("/").append(fileinfos.name));
                // 文件后缀
                else if (NULL != strstr(fileinfos.name, suffix.c_str()))
                    m_file_vector_.push_back(path.assign(dirPath).append("/").append(fileinfos.name));

                LOG_DEBUG("director list filename={}, size={}, suffix={}", fileinfos.name, fileinfos.size,
                                 suffix);
            }
            // 寻找下一个，成功返回0，否则-1
        } while (_findnext(hfile, &fileinfos) == 0);
        _findclose(hfile);
        LOG_DEBUG("pcap directory list success. path={},fileNums={}.", dirPath, m_file_vector_.size());
        return true;
    }
#else
    DIR *dir = opendir(dirPath.c_str());
    if (dir == NULL) {
        LOG_ERROR("opendir error. dir={}", dirPath);
        return false;
    }
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
            std::string dirSub = dirPath + "/" + entry->d_name;
            pcapfile_dir_list(dirSub, suffix);
            LOG_DEBUG("director list subdir={}.", dirSub);
        } else {
            std::string name = entry->d_name;
            std::string path_name = dirPath + "/" + name;
            // 文件名,匹配所有文件
            if (strcmp(suffix.c_str(), "*") == 0)
                m_file_vector_.push_back(path_name);
            // 文件后缀
            else if (NULL != strstr(name.c_str(), suffix.c_str()))
                m_file_vector_.push_back(path_name);
            LOG_DEBUG("director list filename={},suffix={}", path_name, suffix);
        }
    }
    closedir(dir);

#endif

    LOG_DEBUG("director closed! path={},current file Nums={}.", dirPath, m_file_vector_.size());
    return true;
}

/**
 * @brief 目录所有相关文件获取
 * @param const std::string& dirPath,            目录路径
 * @param std::vector<std::string>& fileLists    获取文件列表
 *
 * @return bool
 *
 */
bool netpcap_file::pcapfile_dir_get(const std::string &dirPath, std::vector<std::string> &fileLists)
{
    if (false == pcapfile_dir_list(dirPath, "*")) {
        LOG_ERROR("director list failed! dirPath={}.", dirPath);
        return false;
    }
    try {
        auto itemor = m_file_vector_.begin();
        for (; itemor != m_file_vector_.end(); ++itemor) {
            fileLists.push_back(*itemor);
        }
        return true;
    } catch (std::exception &e) {
        LOG_ERROR("director get failed! err={}.", e.what());
    }
    return true;
}

/**
 * @brief 目录文件获取
 * @param const std::string& dirPath,            目录路径
 * @param suffix                                 后缀, 例如: ".pcap"
 * @param std::vector<std::string>& fileLists    获取文件列表
 *
 * @return bool
 *
 */
bool netpcap_file::pcapfile_dir_get(const std::string &dirPath, std::string suffix, std::vector<std::string> &fileLists)
{
    if (false == pcapfile_dir_list(dirPath, suffix)) {
        LOG_ERROR("director list failed! dirPath={}.", dirPath);
        return false;
    }
    try {
        auto itemor = m_file_vector_.begin();
        for (; itemor != m_file_vector_.end(); ++itemor) {
            fileLists.push_back(*itemor);
        }
        return true;
    } catch (std::exception &e) {
        LOG_ERROR("director get failed! err={}.", e.what());
    }
    return true;
}

/**
 * @brief 获取当前APP全路径
 *
 * @return bool
 *
 */
std::string netpcap_file::pcapfile_get_app_path(void)
{
    char path[MAX_PATH] = {0};
#ifdef _WIN32
    GetModuleFileNameA(NULL, path, sizeof(path));
#else
    readlink("/proc/self/exe", path, sizeof(path));
#endif
    return path;
}

/**
 * @brief 获取当前APP名称
 *
 * @return bool
 *
 */
std::string netpcap_file::pcapfile_get_app_name(void)
{
    std::string path = pcapfile_get_app_path();
    if (path.empty()) {
        return path;
    }
    std::string::size_type offset = path.length() - 1;
    for (; path[offset] != DIR_SEPARATOR && offset != 0; --offset)
        ;
    return path.substr(offset + 1);
}

/**
 * @brief 获取当前APP目录,末尾带'/'
 *
 * @return bool
 *
 */
std::string netpcap_file::pcapfile_get_app_dir(void)
{
    std::string path = pcapfile_get_app_path();
    while (*(--path.end()) != DIR_SEPARATOR) {
        path.pop_back();
    }
    return std::move(path);
}

/**
 * @brief 目录是否存在
 * @param const std::string& dirPath,            目录路径
 * @return bool
 *
 */
bool netpcap_file::pcapfile_dir_is_exist(const std::string &dirPath)
{
#ifdef _WIN32
    WIN32_FIND_DATAA wfd;
    HANDLE hFile = FindFirstFileA(dirPath.c_str(), &wfd);

    if ((hFile != INVALID_HANDLE_VALUE) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
        FindClose(hFile);
        return true;
    }
    FindClose(hFile);
#else
    DIR *dir = NULL;
    dir = opendir(dirPath.c_str());
    if (dir != NULL) {
        closedir(dir);
        return true;
    }
#endif
    return false;
}

/**
 * @brief 创建
 * @param const std::string& dirPath,            目录路径
 * @return bool
 *
 */
bool netpcap_file::pcapfile_dir_create(const std::string &dirPath)
{
#ifdef _WIN32
    SECURITY_ATTRIBUTES attrib;
    attrib.bInheritHandle = FALSE;
    attrib.lpSecurityDescriptor = NULL;
    attrib.nLength = sizeof(SECURITY_ATTRIBUTES);

    if (dirPath.empty())
        return false;

    return (CreateDirectoryA(dirPath.c_str(), &attrib) == TRUE) ? true : false;
#else
    uint32_t iPos = 0;
    int32_t iRet = 0;
    char acTmp[MAX_PATH + 1] = {0};

    if (dirPath.empty() || dirPath.length() > 512)
        return false;

    memcpy(acTmp, dirPath.c_str(), dirPath.length());

    for (iPos = 1; iPos < dirPath.length(); iPos++) {
        if (acTmp[iPos] == '/') {
            acTmp[iPos] = '\0';

            if (true != pcapfile_dir_is_exist(acTmp)) {
                iRet = mkdir(acTmp, 0777);
                if (0 != iRet) {
                    return false;
                } else {
                    acTmp[iPos] = '/';
                }
            } else {
                acTmp[iPos] = '/';
            }
        }
    }

    if (true != pcapfile_dir_is_exist(acTmp)) {
        iRet = mkdir(acTmp, 0777);
        if (0 != iRet) {
            return false;
        }
    }
    return true;
#endif
}

bool netpcap_file::pcapfile_dir_remove_current(const std::string &dirPath)
{
#ifdef _WIN32
    if (dirPath.empty())
        return false;

    return (RemoveDirectoryA(dirPath.c_str()) == TRUE) ? true : false;
#else
    DIR *pstCurDir = opendir(".");
    struct dirent *pstDirEnt = NULL;
    struct stat stStat;

    if (dirPath.empty()) {
        closedir(pstCurDir);
        return false;
    }

    while ((pstDirEnt = readdir(pstCurDir)) != NULL) {
        stat(pstDirEnt->d_name, &stStat);

        if (strcmp(pstDirEnt->d_name, ".") == 0 || strcmp(pstDirEnt->d_name, "..") == 0) {
            continue;
        }

        if (S_ISDIR(stStat.st_mode)) {
            chdir(pstDirEnt->d_name);
            pcapfile_dir_remove_current(pstDirEnt->d_name);
            chdir("..");
        }
        remove(pstDirEnt->d_name);
    }
    closedir(pstCurDir);
    return true;
#endif
}

bool netpcap_file::pcapfile_dir_remove(const std::string &dirPath)
{
#ifdef _WIN32
    if (dirPath.empty())
        return false;

    return (RemoveDirectoryA(dirPath.c_str()) == TRUE) ? true : false;
#else
    char acOldPath[MAX_PATH] = {0};

    getcwd(acOldPath, MAX_PATH);

    if (chdir(dirPath.c_str()) == -1)
        return false;

    pcapfile_dir_remove_current(dirPath);

    chdir(acOldPath);
    return true;
#endif
}

bool netpcap_file::pcapfile_file_is_exist(const std::string &filePath)
{
#ifdef _WIN32
    WIN32_FIND_DATAA fd;
    HANDLE hFind = FindFirstFileA(filePath.c_str(), &fd);
    if (hFind == INVALID_HANDLE_VALUE || fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        return false;

    FindClose(hFind);
#else
    int ret = access(filePath.c_str(), 0);
    if (0 != ret)
        return false;
#endif

    return true;
}

bool netpcap_file::pcapfile_file_create(const std::string &filePath)
{
    char acPath[MAX_PATH] = {0};
    char *pcChr = nullptr;
    int32_t iPos = 0;

    if (true == pcapfile_file_is_exist(filePath))
        return true;

    memcpy(acPath, filePath.c_str(), filePath.length());

    pcChr = strrchr(acPath, DIR_SEPARATOR);
    if (nullptr == pcChr)
        return false;

    iPos = (uint32_t)(pcChr - acPath);
    acPath[iPos] = '\0';

    if (false == pcapfile_dir_is_exist(acPath)) {
        if (false == pcapfile_dir_create(acPath))
            return false;
    }

#ifdef _WIN32
    DWORD dwFileSize = 0;
    HANDLE hFileHandle = NULL;

    hFileHandle = CreateFileA(filePath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                              OPEN_ALWAYS, 0, 0);
    if (INVALID_HANDLE_VALUE == hFileHandle) {
        std::cout << "FileCreate failed! error=" << GetLastError() << std::endl;
        return false;
    }

    if (0 != GetFileSize(hFileHandle, &dwFileSize)) {
        CloseHandle(hFileHandle);
        return false;
    }

    CloseHandle(hFileHandle);
#else

    FILE *pstFile = nullptr;
    pstFile = fopen(filePath.c_str(), "ab+");
    if (nullptr == pstFile)
        return false;

    fclose(pstFile);
#endif
    return true;
}

bool netpcap_file::pcapfile_file_remove(const std::string &dirPath)
{
#ifdef _WIN32
    return (DeleteFileA(dirPath.c_str()) == TRUE) ? true : false;
#else
    remove(dirPath.c_str());
    return true;
#endif
}

void netpcap_file::pcapfile_set_dir_path(const std::string &dir)
{
    m_dir_path_ = dir;
}

void netpcap_file::pcapfile_set_file_name(const std::string &filename)
{
    m_file_name_ = filename;
}

/**
 * @brief 在m_dir_path_目录中创建一个新的文件
 * @return std::string 新文件的完整文件名（含路径），创建文件失败，返回空字符串
 *
 */
std::string netpcap_file::pcapfile_open_new_file()
{
    m_file_index_++;
    std::string file_path = m_dir_path_ + std::to_string(m_file_index_) + "_" + pcapfile_timestamp() + ".pcap";
    if ((false == pcapfile_file_is_exist(file_path)) &&
        (false == pcapfile_file_create(file_path))) {
        return std::string("");
    }
    m_file_nums_ = 0;
    return file_path;
}

/*
ofstream  //文件写操作,内存写入存储设备(文件)  输出流
通常我们所说的对一个文件进行写操作，就是把内存里的内容，也就是缓冲区的内容写到硬盘，可以将标准输出设备理解为显示器
ifstream      //文件读操作,存储设备到内存.       输入流
通常我们所说对一个文件读操作，就是把存在硬盘的内容写到内存中，也就是缓冲区
fstream      //读写操作,对打开的文件可进行读写.   前两者的结合
//文件打开方式选项
ios::in　　= 0x01,　//供读，文件不存在则创建(ifstream默认的打开方 式)
ios::out　 = 0x02,　//供写，文件不存在则创建，若文件已存在则清空原内容(ofstream默认的打开方式)
ios::ate　 = 0x04,　//文件打开时，指针在文件最后。可改变指针的位置，常和in、out联合使用
ios::app　 = 0x08,　//供写，文件不存在则创建，若文件已存在则在原文件内容后写入 新的内容，指针位置总在最后
ios::trunc　= 0x10,　     // 在读写前先将文件长度截断为0（默认）
ios::nocreate　 = 0x20,  //文件不存在时产生错误，常和in或app联合使用
ios::noreplace = 0x40,   //文件存在时产生错误，常和out联合使用
ios::binary　　 = 0x80　 //二进制格式文件

//文件保护方式选择项
filebuf::openprot;　　　//默认的兼容共享方式
filebuf::sh_none;　　　 //独占，不共享
filebuf::sh_read;　　　 //读共享
filebuf::sh_write;　　　//写共享

通过移动文件读写指针，可在文件指定位置进行读写。
seekg(绝对位置);　　　　　　//绝对移动，　　　　//输入流操作
seekg(相对位置,参照位置);　 //相对操作
tellg();　　　　　　　　　　//返回当前指针位置
seekp(绝对位置);　　　　　　//绝对移动，　　　　//输出流操作
seekp(相对位置,参照位置);　 //相对操作　　　
tellp();　　　　　　　　　　//返回当前指针位置

参照位置： mysql
ios::beg　 = 0　　　　　　 //相对于文件头
ios::cur 　= 1　　　　　　 //相对于当前位置
ios::end　 = 2　　　　　　 //相对于文件尾
*/

} // namespace secsmart_pcap
